#include "ini_parser.h"

static PSIniSect s_ini_pstGetSect(PSIniFile pstIniFile, const char *pcSect);
static int s_ini_iLoadFromString(PSIniFile pstIniFile, const char * pcConf);

static PSIniSect s_sect_pstCreate(const char * pcSect);
static void s_sect_vClear(PSIniSect pstSect);
static void s_sect_vPutItem(PSIniSect pstSect, const char* pcItem, const char* pcValue);
static PSIniItem s_sect_pstGetItem(PSIniSect pstSect, const char* pcItem);

static PSIniItem s_item_pstCreate(const char* pcItem, const char* pcValue);

static char * s_pcTrim(char *pcStr);


PSIniFile
init_pstCreate()
{
	PSIniFile pstIniFile = (PSIniFile)malloc(sizeof(SIniFile));
	if(NULL == pstIniFile) {
		printf("s_ini_pstLoadFromString pstIniFile malloc failed\n");
		return NULL;
	}

	pstIniFile->m_pstSectList = JList_pstCreate();

	return pstIniFile;
}

int ini_iLoadFromFile(PSIniFile pstIniFile, const char *pcFile)
{
	if(NULL == pcFile) {
		printf("open_ini_file pcFile==NULL\n");
		return -1;
	}

	FILE *fp = NULL;
	if( (fp = fopen(pcFile, "rb")) == NULL ) {
        printf("open_ini_file Failed to open file %s \n", pcFile);
		return -1;
	}

	//obtain file size
	fseek(fp, 0, SEEK_END);
	int iFileSize = ftell(fp);
	rewind(fp);

	char acIniFile[MAX_INI_FILE_LEN+1] = {0};
	int iReadBytes = fread(&acIniFile, 1, MAX_INI_FILE_LEN, fp);
	if(iFileSize != iReadBytes) {
        printf("open_ini_file read file error\n");
		return -1;
	}
	acIniFile[iReadBytes] = '\0';

	if(fclose(fp) != 0) {
        printf("open_ini_file close file error\n");
		return -1;
	}

	return s_ini_iLoadFromString(pstIniFile, acIniFile);
}

static int s_ini_iLoadFromString(PSIniFile pstIniFile, const char * pcConf)
{
	if(NULL == pcConf) {
        printf("s_ini_pstLoadFromString NULL == pcConf\n");
		return -1;
	}


	int iLen = strlen(pcConf);
	int iIndex = 0;
	char acLine[MAX_LINE_LEN] = {0};

	char acSect[MAX_NAME_LEN] = {0};
	char *pcStart = NULL;
	char *pcEnd = NULL;

	for (int i=0; i<=iLen; i++)
	{
		char ch = pcConf[i];
		if(ch == '\r' || ch == '\n' || ch == 0 || i == iLen)
		{
			if(iIndex == 0) 
				continue;

			acLine[iIndex] = 0;
			
			pcEnd = ::strstr(acLine, "##");
			if(NULL != pcEnd)
			{
				*pcEnd = 0;
			}

			pcStart = s_pcTrim(acLine);
			if(0 == ::strlen(pcStart))
			{
				iIndex = 0;
				continue;
			}

			if('[' == *pcStart)
			{
				//It's a section name
				pcEnd = ::strchr(pcStart, ']');
				if(NULL == pcEnd)
				{
					printf("s_ini_pstLoadFromString Can't find ']'\n");
					return -1;
				}

				pcStart++;
				*pcEnd = '\0';
				strncpy(acSect, pcStart, MAX_NAME_LEN-1);
				acSect[MAX_NAME_LEN-1] = '\0';
			}
			else
			{
				//It's an item here.
				pcEnd = strchr(pcStart, '=');
				if(NULL == pcEnd)
				{
					ini_iPutItemString(pstIniFile, acSect, s_pcTrim(pcStart), "");
				}

				*pcEnd = '\0';
				ini_iPutItemString(pstIniFile, acSect, s_pcTrim(pcStart), s_pcTrim(++pcEnd));
			}

			iIndex = 0;
		}
		else
		{
			acLine[ iIndex++ ] = ch;
		}
	} //for

	return 0;
}

int ini_iPutItemString(PSIniFile pstIniFile, const char* pcSect, const char* pcItem, const char* pcValue)
{
	if(NULL == pstIniFile || NULL == pcSect || NULL == pcItem || NULL == pcValue) {
		return -1;
	}

	PSIniSect pstSect = s_ini_pstGetSect(pstIniFile, pcSect);
	if(NULL == pstSect)
	{
		pstSect = s_sect_pstCreate(pcSect);

		if(pstIniFile->m_pstSectList == NULL)
		{
			printf("pstIniFile->m_pstSectList == NULL\n");
		}

		JList_add(pstIniFile->m_pstSectList, pstSect);
	}
	s_sect_vPutItem(pstSect, pcItem, pcValue);

	return 0;
}

int 
int_iPutItemInteger(PSIniFile pstIniFile, const char* pcSect, const char* pcItem, int iValue)
{
	char acVal[MAX_INTEGER_LEN] = {0};
	sprintf(acVal, "%d", iValue);
	return ini_iPutItemString(pstIniFile, pcSect, pcItem, acVal);
}

const char *
ini_pcGetItemString(PSIniFile pstIniFile, const char* pcSect, const char* pcItem, const char* pcDefault)
{
	if(NULL == pstIniFile || NULL == pcSect || NULL == pcItem) {
		return pcDefault;
	}

	PSIniSect pstSect = s_ini_pstGetSect(pstIniFile, pcSect);
	if(NULL == pstSect) {
		return pcDefault;
	}

	PSIniItem pstItem = s_sect_pstGetItem(pstSect, pcItem);
	if(NULL == pstItem) {
		return pcDefault;
	}

	return pstItem->m_acVal;
}

int 
ini_iGetItemInteger(PSIniFile pstIniFile, const char* pcSect, const char* pcItem, int iDefault)
{
	const char* pcVal = ini_pcGetItemString(pstIniFile, pcSect, pcItem, NULL);
	if(NULL == pcVal)
	{
		return iDefault;
	}
	else
	{
		return ::atoi(pcVal);
	}
}

static PSIniSect s_ini_pstGetSect(PSIniFile pstIniFile, const char *pcSect)
{
	SListNode *pstNode = JList_first(pstIniFile->m_pstSectList);
	while (NULL != pstNode)
	{
		PSIniSect pstSect = (PSIniSect)pstNode->m_pvData;
		if(0 == strncmp(pcSect, pstSect->m_acName, MAX_NAME_LEN))
		{
			return pstSect;
		}

		pstNode = pstNode->m_pstNext;
	}
	return NULL;
}

int 
ini_iSaveToFile(PSIniFile pstIniFile, const char* pcFileName)
{
	if(NULL == pcFileName) {
		printf("ini_iSaveToFile NULL == pcFileName\n");
		return -1;
	}

	FILE * f = fopen(pcFileName, "wb+");
	if(NULL == f) {
		printf("ini_iSaveToFile open file error\n");
		return -1;
	}


	SListNode *pstSectNode = JList_first(pstIniFile->m_pstSectList);
	while (NULL != pstSectNode)
	{
		PSIniSect pstSect = (PSIniSect)pstSectNode->m_pvData;
		fputc('[', f);
		fputs(pstSect->m_acName, f);
		fputc(']', f);
		fputs("\r\n", f);

		SListNode *pstItemNode = JList_first(pstSect->m_pstItemList);
		while (NULL != pstItemNode)
		{
			PSIniItem pstItem = (PSIniItem)pstItemNode->m_pvData;
			fputs(pstItem->m_acName, f);
			fputc('=', f);
			fputs(pstItem->m_acVal, f);
			fputs("\r\n", f);

			pstItemNode = pstItemNode->m_pstNext;
		}

		pstSectNode = pstSectNode->m_pstNext;
	}


	if(fclose(f) != 0) {
		printf("ini_iSaveToFile close file error\n");
		return -1;
	}

	return 0;
}

void 
ini_vRemoveSect(PSIniFile pstIniFile, const char* pcSect)
{
    if(NULL == pstIniFile || NULL == pcSect) {
        printf("ini_vRemoveSect\n");
        return ;
    }

	SListNode *pstNode = JList_first(pstIniFile->m_pstSectList);
	while (NULL != pstNode) 
	{
		PSIniSect pstSect = (PSIniSect)pstNode->m_pvData;
		if(0 == strncmp(pcSect, pstSect->m_acName, MAX_NAME_LEN))
        {
            s_sect_vClear(pstSect);
            JList_del(pstIniFile->m_pstSectList, pstNode);
            break;
        }

		pstNode = pstNode->m_pstNext;
	}
}

void
ini_vRemoveItem(PSIniFile pstIniFile, const char* pcSect, const char* pcItem)
{
    if(NULL == pstIniFile || NULL == pcSect || NULL == pcItem) {
        printf("ini_vRemoveItem input error\n");
        return ;
    }

    PSIniSect pstSect = s_ini_pstGetSect(pstIniFile, pcSect);
    if(NULL == pstSect) {
        printf("ini_vRemoveItem get section error\n");
        return ;
    }

	SListNode *pstNode = JList_first(pstSect->m_pstItemList);
	while (NULL != pstNode) 
	{
		PSIniItem pstItem = (PSIniItem)pstNode->m_pvData;
        if(0 == strncmp(pcItem, pstItem->m_acName, MAX_NAME_LEN))
        {
            free(pstItem);
            JList_del(pstSect->m_pstItemList, pstNode);
            break;
        }

		pstNode = pstNode->m_pstNext;
	}
}

void ini_vClear(PSIniFile pstIniFile)
{
	SListNode *pstNode = JList_first(pstIniFile->m_pstSectList);
	while (NULL != pstNode) 
	{
		PSIniSect pstSect = (PSIniSect)pstNode->m_pvData;
		s_sect_vClear(pstSect);

		pstNode = pstNode->m_pstNext;
	}
	JList_free(pstIniFile->m_pstSectList);
}


/*
 * section
 */
static PSIniSect s_sect_pstCreate(const char * pcSect)
{
	PSIniSect pstSect = (PSIniSect)malloc(sizeof(SIniSect));
	strncpy(pstSect->m_acName, pcSect, MAX_NAME_LEN-1);
	pstSect->m_acName[MAX_NAME_LEN-1] = '\0';
	pstSect->m_pstItemList = JList_pstCreate();
	return pstSect;
}

static void s_sect_vClear(PSIniSect pstSect)
{
	SListNode *pstNode = JList_first(pstSect->m_pstItemList);
	while (NULL != pstNode) 
	{
		PSIniItem pstItem = (PSIniItem)pstNode->m_pvData;
		free(pstItem);

		pstNode = pstNode->m_pstNext;
	}
	JList_free(pstSect->m_pstItemList);
}

static void s_sect_vPutItem(PSIniSect pstSect, const char* pcItem, const char* pcValue)
{
	SListNode *pstNode = JList_first(pstSect->m_pstItemList);
	while (NULL != pstNode) 
	{
		PSIniItem pstItem = (PSIniItem)pstNode->m_pvData;
		if(0 == strncmp(pcItem, pstItem->m_acName, MAX_NAME_LEN))
		{
			//if item exist, update it
			strncpy(pstItem->m_acVal, pcValue, MAX_VAL_LEN-1);
			pstItem->m_acVal[MAX_VAL_LEN-1] = '\0';
			break;
		}
		pstNode = pstNode->m_pstNext;
	}
	if(NULL == pstNode)
	{
		PSIniItem pstItem = s_item_pstCreate(pcItem, pcValue);
		JList_add(pstSect->m_pstItemList, pstItem);
	}
}

static PSIniItem s_sect_pstGetItem(PSIniSect pstSect, const char* pcItem)
{
	SListNode *pstNode = JList_first(pstSect->m_pstItemList);
	while (NULL != pstNode) 
	{
		PSIniItem pstItem = (PSIniItem)pstNode->m_pvData;
		if(0 == strncmp(pcItem, pstItem->m_acName, MAX_NAME_LEN))
		{
			return pstItem;
		}

		pstNode = pstNode->m_pstNext;
	}
	return NULL;
}


/*
 * Item
 */
static PSIniItem s_item_pstCreate(const char* pcItem, const char* pcValue)
{
	PSIniItem pstItem = (PSIniItem)malloc(sizeof(SIniItem));
	strncpy(pstItem->m_acName, pcItem, MAX_NAME_LEN-1);
	strncpy(pstItem->m_acVal, pcValue, MAX_VAL_LEN-1);
	pstItem->m_acName[MAX_NAME_LEN-1] = '\0';
	pstItem->m_acVal[MAX_VAL_LEN-1] = '\0';
	return pstItem;
}


static char *
s_pcTrim(char *pcStr)
{
	if(NULL != pcStr)
	{
		for ( ; *pcStr && ::isspace(*pcStr); ++pcStr) ;

		char *pcLast = pcStr + strlen(pcStr) - 1;
		for ( ; pcLast >= pcStr && ::isspace(*pcLast); --pcLast)
			*pcLast = 0;
	}
	return pcStr;
}


















